package dhadi.model;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.json.JSONException;
import org.json.JSONObject;

public class Player {
	
	private String name;
	private List<Pick> picks; 
	protected Board board;
	
	boolean isDhadiOnMe = false;
	boolean isDhadiByMe = false;
	
	boolean amITheWinner = false;
	boolean amITheLooser = false;
	
	/*Opponent Player*/
	private Player opponent;
	private Message message;
	
	public Player(String name)
	{
		this.name = name;		
	}
	
	void initializePlayer(Board board) {
	    this.board = board;
        this.picks = this.createPics(this, this.board);
	}
	
	public String getName() {
		return this.name;
	}
	
	public void setOpponent(Player opponent) {
		this.opponent = opponent;
	}
	
	public Player getOpponent() {
		return this.opponent;
	}
	
	private List<Pick> createPics(Player player, Board board) {
		List<Pick> picks = new ArrayList<Pick>(11);
		StringBuilder pickName = new StringBuilder();
		for (int i = 0; i < 11; i++)
		{
		    pickName.setLength(0);
		    pickName.append(i).append("Pick(").append(player.getName()).append(")");
			picks.add(new Pick(pickName.toString(),player, board));
		}
		return picks;
	}
	
	public List<Pick> getPicks()
	{
		return this.picks;
	}
	
	public List<Pick> getPicksOnBoard() {
		List<Pick> onBoardPicks = new ArrayList<Pick>();
		for (Pick pick : this.picks) {
			if (pick.isOnboard()) onBoardPicks.add(pick);
		}
		return onBoardPicks;
	}
	
	public List<Pick> getUnUsedPicks() {
		List<Pick> onBoardPicks = new ArrayList<Pick>();
		for (Pick pick : this.picks) {
			if (pick.isUnused()) onBoardPicks.add(pick);
		}
		return onBoardPicks;
	}
	
	public List<Pick> getPicksLost() {
		List<Pick> onBoardPicks = new ArrayList<Pick>();
		for (Pick pick : this.picks) {
			if (pick.isDestroyed()) onBoardPicks.add(pick);
		}
		return onBoardPicks;
	}
	
	/**
	 * returns Picks which are part of dhadi formation
	 * for this player
	 * @return
	 */
	public Set<Pick> getDhadiFormationPicks() {
		List<Pick> onBoardPicks = this.getPicksOnBoard();
		Set<Pick> dhadiPicks = new HashSet<Pick>();
		
		Node node;
		for (Pick pick : onBoardPicks) {
			
			if (dhadiPicks.contains(pick)) continue;
			
			node = pick.getNode();
			List<String> triodes = this.board.findTriods(node);
			for (String triode : triodes) {
				List<Pick> triodePicks = this.findPicksOnTriodes(triode,onBoardPicks);
				if (triodePicks.size() == 3)
					for (Pick p2 : triodePicks ) dhadiPicks.add(p2);
			}
		}
		
		return dhadiPicks;
	}
	
	/**
	 * Possible picks are those of player.getOppenant() picks which are on board
	 * and not part of 'dhadi' formation
	 * 
	 * @param player
	 * @return
	 */
	public Set<Pick> getPossiblePicksToKill() {
		Set<Pick> nonDhadiPicks = new HashSet<Pick>();
		List<Pick> opponentPicks = this.getOpponent().getPicksOnBoard();
		Set<Pick> opponentDhadiFormationPicks = this.getOpponent()
				.getDhadiFormationPicks();

		for (Pick pick : opponentPicks) {
			if (!opponentDhadiFormationPicks.contains(pick)) {
				nonDhadiPicks.add(pick);
			}
		}
		
		Set<Pick> possiblePicks = new HashSet<Pick>();
		// Detect and remove a pick such that the
		// following is true.
		//  1. Other than this pick opponent cannot
		//     move any other pick.
		//
		// This is the situation where opponent
		// has only this pick to move, avoid killing
		// such picks.
		List<Pick> movablePicks = this.board.findMovablePicks(this.getOpponent());
		for (Pick nonDhadiPick : nonDhadiPicks) {
		    boolean canOtherPicksMove = false;
		    for (Pick movablePick : movablePicks) {
		        if (nonDhadiPick.equals(movablePick)) continue;
		        if ( movablePick.getNode()!= null &&
		             movablePick.getNode().getAdjusantEmptyNodes().size() > 0) {
		            canOtherPicksMove = true;
		            break;
		        }   
		    }
		    if (canOtherPicksMove) possiblePicks.add(nonDhadiPick);
		}
		return possiblePicks;
	}
	/**
	 * Returns picks that are placed on the triode nodes from the
	 * given onBoardPicks.
	 * 
	 * NOTE: onBoardPicks must be of status Pick.Status.ONBOARD
	 * 
	 * @param triode
	 * @param onBoardPicks
	 * @return
	 */
	protected List<Pick> findPicksOnTriodes(String triode, List<Pick> onBoardPicks) {
		List<Pick> triodePicks = new ArrayList<Pick>();
		String nodeName;
		for (int i=0; i<triode.length(); i++) {
			nodeName = ""+triode.charAt(i);
			for (Pick pick : onBoardPicks) {
				if (pick.getNode().getName().equalsIgnoreCase(nodeName)) triodePicks.add(pick);
			}
		}	
		return triodePicks;
	}
	
	/** 
	 * return true only if this node is part of a dhadi formation
	 * @param node
	 * @return
	 */
	public boolean isPickFormaingDhadi(Pick pick) {
		if (!pick.isOnboard()) return false;
		Node node = pick.getNode();
		if (node == null) return false;
		List<String> triodes = this.board.findTriods(node);
		List<Pick> onBoardPicks = this.getPicksOnBoard();
		for (String triode : triodes) {
			List<Pick> triodePicks = this.findPicksOnTriodes(triode,onBoardPicks);
			if (triodePicks.size() == 3)
				return true;
		}
		return false;
	}
	
	/**
	 * Returns list of Picks which are part of Dhadi formation
	 * @return
	 */
	public List<Pick> findDhadiFormingPicks() {
	    List<Pick> dhadiPicks = new ArrayList<Pick>();
	    List<Pick> onBoardPicks = this.getPicksOnBoard();
	    for (Pick pick : onBoardPicks) {
	        if (this.isPickFormaingDhadi(pick)) {
	            dhadiPicks.add(pick);
	        }
	    }
	    return dhadiPicks;
	}
	
	/**
	 * Returns true if this player can
	 * make at least one move
	 * @return
	 */
	public boolean canMove() {
		
		if (this.getUnUsedPicks().size() > 0) {
			return true;
		}
		for (Pick pick : this.getPicksOnBoard()) {
			if (pick.getNode().getAdjusantEmptyNodes().size() > 0) 
				return true;
		}
		return false;
	}
	
	public boolean canKill() {
		if (this.getPossiblePicksToKill().size() > 0) return true;
		else return false;
	}

	public void makeAMove() {
	}
	
	public void onlyView() {
	}
	
	public void dhadiOnYou() {
		this.isDhadiOnMe = true;
		this.isDhadiByMe = false;
	}
	
	public boolean isDhadiOnYou() {
		return this.isDhadiOnMe;
	}
	
	public void dhadiByYou() {
		this.isDhadiOnMe = false;
		this.isDhadiByMe = true;
	}
	
	public boolean isDhadiByYou() {
		return this.isDhadiByMe;
	}
	
	public void resetDhadiFlags() {
		this.isDhadiOnMe = false;
		this.isDhadiByMe = false;
	}
	
	public void youTheWinner() {
		amITheWinner = true;
		amITheLooser = false;
	}
	
	public boolean areYouWinner() {
		return amITheWinner;
	}
	
	public void youTheLooser() {
		amITheWinner = false;
		amITheLooser = true;
	}
	
	public void setMessage(String message) {
		this.message = new Message(message);
	}
	
	public Message getMessage() {
		return this.message;
	}
	
	public void recordWin() {
		
	}
	
	public void recordLoss() {
		
	}
	
	public String toString() {
		return "Player [name=" + name + "]";
	}

	public JSONObject toJson() throws JSONException {
		JSONObject json = new JSONObject();
		json.put("Name", this.name);
		return json;
	}
	
	public static Player fromJson(String jsonString) throws JSONException {
	    JSONObject json = new JSONObject(jsonString);
	    String name = json.getString("Name");
	    return new Player(name);
	}
	
	public boolean equals(Object otherPlayer) {
	    if (!(otherPlayer instanceof Player))
	        return false;
	    return this.name.equalsIgnoreCase(((Player)otherPlayer).getName());
	}
}
